iT邦幫忙

2023 iThome 鐵人賽

DAY 26
0
Kotlin

讓 Kotlin 程式碼更道地 - 談 Effective Kotlin 與相關的 Design Pattern系列 第 26

D26: Kotlin 效能 - 給函式俄羅斯娃娃使用 inline 修飾子

  • 分享至 

  • xImage
  •  

Item 48 : 給高階函數使用 inline 修飾子

所謂高階函式是接受其他函式作為參數或返回函式的函式,可以理解成函式裡面還有函式,所以稱函式俄羅斯娃娃。Kotlin 中的 inline 函式是為了效能考量而引入的特性。當我們使用高階函式時,會有額外的執行期時開銷,因為在 JVM 本身沒有 function type 的概念。inline function 這題,朱凱老師講的很好,看完就滿夠的

Yes

兩層的函數俄羅斯娃娃考慮用 inline

所以在執行時,傳遞的函式參數需要生成額外的物件來包裝 function type 。這些開銷在頻繁呼叫的情境下,尤其是在迴圈內,可能導致效能問題。

所以幾乎 Kotlin 內建的高階函式(如 Collection 的 filter, map) 都是 inline 函式

inline fun repeat(times: Int,action: (Int) -> Unit) {
  for (index in 0 until times) {
    action(index)
  }
}

inline 關鍵字告訴編譯器,當該函式被呼叫時,不要真的呼叫它,而是將函式的程式碼嵌入到呼叫處。這樣,我們就可以避免因函式呼叫而產生的開銷。

在編譯之後

repeat(10) {
    print(it)
}

//after compilation

for (index in 0 until 10) {
    print(index)
}

使用時機

  1. 不是所有函式都應該被標記為 inline。只有那些真正會造成效能問題的高階函式或是頻繁呼叫的函式才應該考慮使用 inline
  2. inline 函式的程式碼會被嵌入到每個呼叫處,這可能會增加生成的 Artifact 大小。因此,要根據具體情境權衡是否使用 inline

三層以上函式俄羅斯娃娃需要 noinline

當你在使用 inline 函式時,有時可能會遇到需要更細緻控制 lambda 行為的情境。在這些情境中,noinline 和 crossinline 這兩個修飾詞會派上用場。以下我們來介紹它們:

noinline

當一個 inline 函式接受多個 lambda 參數時,你可能不想要所有的 lambda 都被嵌入。這時,你可以使用 noinline 修飾詞來指示編譯器哪些 lambda 不應該被嵌入。而常見的是把傳入的 lambda 再當作回傳值,因為這時如果他是 inline 就無法當作回傳值(因為該 function type 被展開)。這時 IDE 也會提示你 noinline

crossinline

在 inline 函式中,有時你會遇到需要在另外一個執行環境(例如異步回調)中呼叫 lambda 的情境。但由於 lambda 可能包含 return 關鍵字,這可能會導致整個外部函式(而不只是 lambda 本身)提早結束。為了避免這種情況,Kotlin 提供了 crossinline 修飾詞,使得在該 lambda 內不能有非局部的返回。

使用時機: 當你需要確保 inline lambda 不會非局部返回時

inline fun runAsync(crossinline body: () -> Unit) {
    Thread {
        body()
    }.start()
}


fun main() {
    runAsync {
        println("This runs in another thread.")
        // return  // This would be an error because of 'crossinline'
    }
}

在上述例子中,如果沒有 crossinline,lambda 內的 return 會導致 main 函式提早結束。但由於我們使用了 crossinline,所以這個 return 是不被允許的。

總之,noinline 和 crossinline 都是為了提供更多的控制以滿足特定的需求而設計的修飾詞。

noinline, crossinline → IntelliJ 會告訴你

IntelliJ 或 Android 其實會幫你注意這些狀況。所以這兩個只要相信 IDE 的題示就可以了。

每日一推(G)I-DLE

與 PSY大叔合作的舞台,也是很炸!

Yes


上一篇
D25: Kotlin 效能 - 重物件挪抬與 Lazy 延遲初始化
下一篇
D27: 偏好使⽤ Sequences 來取代大量且有多次操作的集合
系列文
讓 Kotlin 程式碼更道地 - 談 Effective Kotlin 與相關的 Design Pattern30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言